package com.platform.uaa.service;

import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.platform.comm.enums.YnEnum;
import com.platform.comm.exceptions.RestApiException;
import com.platform.config.properties.CustomSecurityProperties;
import com.platform.core.entity.SysDepart;
import com.platform.core.service.BaseEntityBiz;
import com.platform.core.service.SysDepartService;
import com.platform.core.service.SysUserService;
import com.platform.dto.RoleQo;
import com.platform.dto.UserDetail;
import com.platform.enums.CommFlagType;
import com.platform.enums.RoleEnum;
import io.jsonwebtoken.lang.Collections;
import lombok.extern.log4j.Log4j2;
import org.springframework.context.MessageSource;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 描述：自定义UserDetailsService，从数据库读取用户信息，实现登录验证
 *
 * @author coder
 */
@Component("customUserDetailsService")
@Log4j2
public class CustomUserDetailsServiceImpl implements UserDetailsService {

    private final SysUserService sysUserService;
    private final CustomSecurityProperties securityProperties;
    private final MessageSource messageSource;
    private final SysDepartService sysDepartService;


    public CustomUserDetailsServiceImpl(SysUserService sysUserService,
                                        CustomSecurityProperties securityProperties,
                                        MessageSource messageSource, SysDepartService sysDepartService) {
        this.sysUserService = sysUserService;
        this.securityProperties = securityProperties;
        this.messageSource = messageSource;
        this.sysDepartService = sysDepartService;
    }

    /**
     * 认证过程中 - 根据登录信息获取用户详细信息
     *
     * @param username 登录用户输入的用户名
     * @return 用户信息
     * @throws UsernameNotFoundException 异常
     */
    @Override
    public UserDetails loadUserByUsername(String username) {
        try {
            return findCustomUserDetailsByUserName(username);
        } catch (DisabledException | UsernameNotFoundException | BadCredentialsException e) {
            if (e instanceof BadCredentialsException) {
                throw new RestApiException("用户名或密码错误");
            } else {
                throw new RestApiException(e.getMessage());
            }
        }

    }

    private CustomUserDetails findCustomUserDetailsByUserName(String username) throws UsernameNotFoundException {
        //根据用户输入的用户信息，查询数据库中已注册用户信息
        UserDetail userDetail = sysUserService.findByUsername(username);
        if (userDetail == null || Strings.isNullOrEmpty(userDetail.getId())) {
            //throw new UsernameNotFoundException(String.format("用户【%s】不存在", username));
            log.error(String.format("用户【%s】不存在", username));
            throw new UsernameNotFoundException("用户名或密码错误");
        } else {
            boolean enabled = isEnabled(userDetail);
            if (!enabled) {
                //throw new DisabledException(String.format("用户【%s】已被禁用。", username));
                log.error(String.format("用户【%s】已被禁用。", username));
                throw new UsernameNotFoundException("用户名或密码错误");
            }

            if (Objects.isNull(userDetail.getDepartId())) {
                //throw new DisabledException(String.format("用户【%s】所在部门不存在。", username));
                log.error(String.format("用户【%s】所在部门不存在。", username));
                throw new UsernameNotFoundException("用户名或密码错误");
            } else {
                //判断用户所在部门是否被禁用
                SysDepart sysDepart = sysDepartService.fetchById(userDetail.getDepartId());
                if (Objects.isNull(sysDepart) || CommFlagType.Disabled.getFvalue().equals(sysDepart.getStatus().getFvalue())) {
                   // throw new DisabledException(String.format("用户【%s】所在部门已被禁用。", username));
                    log.error(String.format("用户【%s】所在部门已被禁用。", username));
                    throw new UsernameNotFoundException("用户名或密码错误");
                }
            }

            boolean credentialsNonExpired = isCredentialsNonExpired(userDetail);
            if (!credentialsNonExpired) {
                log.error(String.format("密码已过期，请联系管理员修改密码。"));
                throw new CredentialsExpiredException("用户名或密码错误");
            }

            if (userDetail.getStatus().equals(CommFlagType.Locked)) {
                //throw new LockedException(String.format("用户【%s】被锁定，请联系管理员恢复账号", username));
                log.error(String.format("用户【%s】被锁定，请联系管理员恢复账号", username));
                throw new UsernameNotFoundException("用户名或密码错误");
            }

            Set<RoleQo> roles = Sets.newHashSet(userDetail.getRoles());

            if (Collections.isEmpty(roles)) {
                //throw new LockedException(String.format("用户【%s】没有关联角色，请联系管理员", username));
                log.error(String.format("用户【%s】没有关联角色，请联系管理员", username));
                throw new UsernameNotFoundException("用户名或密码错误");
            }

            boolean isAdminFlag = roles.parallelStream()
                    .anyMatch(role -> role.getCode().equals(RoleEnum.ROLE_ADMIN.name()) ||
                            role.getRoute().contains(RoleEnum.ROLE_ADMIN.name()));

            Set<GrantedAuthority> grantedAuthorities = generateAuthorities(roles);
            CustomUserDetails customUserDetails = new CustomUserDetails(userDetail.getUsername(),
                    userDetail.getPassword(), grantedAuthorities);
            customUserDetails.setId(userDetail.getId());
            customUserDetails.setLastPasswordReset(userDetail.getPwdModifyDate());
            customUserDetails.setDepartId(userDetail.getDepartId());
            customUserDetails.setDepartName(userDetail.getDepartName());
            customUserDetails.setDepartKind(userDetail.getDepartKind());
            customUserDetails.setRealName(userDetail.getRealName());
            customUserDetails.setFirst(Objects.isNull(userDetail.getFirstLoginDate()));
            // 设置是否为管理员
            customUserDetails.setAdmin(isAdminFlag);

            return customUserDetails;
        }
    }

    private Set<GrantedAuthority> generateAuthorities(Set<RoleQo> roles) {
        return roles.parallelStream()
                .map(roleQo -> StringUtils.replace(roleQo.getRoute(), BaseEntityBiz.ROUTE_SEPARATOR, ","))
                .map(AuthorityUtils::commaSeparatedStringToAuthorityList)
                .flatMap(Collection::stream)
                .collect(Collectors.toSet());
    }

    private boolean isEnabled(UserDetail userDetail) {
        return userDetail.getDelFlag().equals(YnEnum.N)
                && (userDetail.getStatus().equals(CommFlagType.Enabled) || userDetail.getStatus().equals(CommFlagType.Initial));
    }

    private boolean isCredentialsNonExpired(UserDetail userDetail) {
        if (userDetail.getPwdLimitDays() == null
                || userDetail.getPwdLimitDays() <= 0
                || userDetail.getPwdModifyDate() == null) {
            return true;
        } else {
            // 密码已使用天数
            LocalDateTime pwdDate = userDetail.getPwdModifyDate().plus(userDetail.getPwdLimitDays(), ChronoUnit.DAYS);
            return pwdDate.isBefore(LocalDateTime.now());
        }
    }
}
